package de.mrapp.ohos_util;

import de.mrapp.util.Condition;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.global.configuration.Configuration;


public class DisplayUtil {

    /**
     * Contains all possible orientations of devices.
     */
    public enum Orientation {

        /**
         * When the device is in portrait mode.
         */
        PORTRAIT,

        /**
         * When the device is in landscape mode.
         */
        LANDSCAPE,

        /**
         * When the width and height of the device's display are equal.
         */
        SQUARE

    }

    /**
     * Contains all possible types of devices, depending on their display size.
     */
    public enum DeviceType {

        /**
         * When the device is a phone, e.g. 4-, 5- or 6-inch devices like
         * 6.
         */
        PHONE("phone"),

        /**
         * When the device is a small tablet (phablet), e.g. a 7-inch devices like
         * 7.
         */
        PHABLET("phablet"),

        /**
         * When the device is a tablet, e.g. a 10-inch devices like
         */
        TABLET("tablet");

        /**
         * The value of the device type.
         */
        private String value;

        /**
         * Creates a new device type.
         *
         * @param value The value of the device type as a {@link String}
         */
        DeviceType(final String value) {
            this.value = value;
        }

        /**
         * Returns the value of the device type.
         *
         * @return The value of the device type as a {@link String}
         */
        public final String getValue() {
            return value;
        }

        /**
         * Returns the device type, which corresponds to a specific value.
         *
         * @param value The value of the device type, which should be returned, as a {@link String}
         * @return The device type, which corresponds to the given value, as a value of the enum
         * {@link DeviceType}
         * @throws IllegalArgumentException
         */
        public static DeviceType fromValue(final String value) {
            for (DeviceType deviceType : values()) {
                if (deviceType.getValue().equals(value)) {
                    return deviceType;
                }
            }

            throw new IllegalArgumentException("Invalid enum value: " + value);
        }

    }

    /**
     * The ratio, which can be used to convert values, which are measured in pixels, into values,
     * which are measured in dp, and vice versa.
     */

    protected static final float PIXEL_DP_RATIO = 160.0f;

    /**
     * Creates a new utility class, which provides static methods, which are related to a device's
     * display metrics.
     */
    private DisplayUtil() {

    }

    /**
     * Converts an {@link Integer} value, which is measured in pixels, into a value, which is
     * measured in dp.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @param pixels  The pixel value, which should be converted, as an {@link Integer} value
     * @return The calculated dp value as an {@link Integer} value. The value might be rounded
     */
    public static int pixelsToDp(final Context context, final int pixels) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return Math.round(pixels / (display.getAttributes().densityDpi / PIXEL_DP_RATIO));
    }

    /**
     * Converts an {@link Long} value, which is measured in pixels, into a value, which is measured
     * in dp.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @param pixels  The pixel value, which should be converted, as an {@link Long} value
     * @return The calculated dp value as an {@link Long} value. The value might be rounded
     */
    public static long pixelsToDp(final Context context, final long pixels) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return Math.round(pixels / (display.getAttributes().densityDpi / PIXEL_DP_RATIO));
    }

    /**
     * Converts a {@link Float} value, which is measured in pixels, into a value, which is measured
     * in dp.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @param pixels  The pixel value, which should be converted, as a {@link Float} value
     * @return The calculated dp value as a {@link Float} value
     */
    public static float pixelsToDp(final Context context, final float pixels) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return pixels / (display.getAttributes().densityDpi / PIXEL_DP_RATIO);
    }

    /**
     * Converts a {@link Double} value, which is measured in pixels, into a value, which is measured
     * in dp.
     *
     * @param context The context, which should be used to, as an instance of the class {@link Context}.
     *                The context may not be null
     * @param pixels  The pixel value, which should be converted, as a {@link Double} value
     * @return The calculated dp value as a {@link Double} value
     */
    public static double pixelsToDp(final Context context, final double pixels) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return pixels / (display.getAttributes().densityDpi / PIXEL_DP_RATIO);
    }

    /**
     * Converts an {@link Integer} value, which is measured in dp, into a value, which is measured
     * in pixels.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @param dp      The dp value, which should be converted, as an {@link Integer} value
     * @return The calculated pixel value as an {@link Integer} value. The value might be rounded
     */
    public static int dpToPixels(final Context context, final int dp) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return Math.round(dp * (display.getAttributes().densityDpi / PIXEL_DP_RATIO));
    }

    /**
     * Converts an {@link Integer} value, which is measured in dp, into a value, which is measured
     * in pixels.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @param dp      The dp value, which should be converted, as an {@link Integer} value
     * @return The calculated pixel value as an {@link Integer} value. The value might be rounded
     */
    public static long dpToPixels(final Context context, final long dp) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return Math.round(dp * (display.getAttributes().densityDpi / PIXEL_DP_RATIO));
    }

    /**
     * Converts a {@link Float} value, which is measured in dp, into a value, which is measured in
     * pixels.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @param dp      The dp value, which should be converted, as a {@link Float} value
     * @return The calculated pixel value as a {@link Float} value
     */
    public static float dpToPixels(final Context context, final float dp) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return dp * (display.getAttributes().densityDpi / PIXEL_DP_RATIO);
    }

    /**
     * Converts a {@link Double} value, which is measured in dp, into a value, which is measured in
     * pixels.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @param dp      The dp value, which should be converted, as a {@link Double} value
     * @return The calculated pixel value as a {@link Double} value
     */
    public static double dpToPixels(final Context context, final double dp) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return dp * (display.getAttributes().densityDpi / PIXEL_DP_RATIO);
    }

    /**
     * Returns the orientation of the device.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @return The orientation of the device as a value of the enum {@link Orientation}. The
     * orientation may either be <code>PORTRAIT</code>, <code>LANDSCAPE</code> or
     * <code>SQUARE</code>
     */
    public static Orientation getOrientation(final Context context) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Configuration configuration = context.getResourceManager().getConfiguration();
        int orientation = configuration.direction;

        if (orientation == Configuration.DIRECTION_UNDEFINED) {
            int width = getDisplayWidth(context);
            int height = getDisplayHeight(context);

            if (width > height) {
                return Orientation.LANDSCAPE;
            } else if (width < height) {
                return Orientation.PORTRAIT;
            } else {
                return Orientation.SQUARE;
            }
        } else if (orientation == Configuration.DIRECTION_HORIZONTAL) {
            return Orientation.LANDSCAPE;
        } else if (orientation == Configuration.DIRECTION_VERTICAL) {
            return Orientation.PORTRAIT;
        } else {
            return Orientation.SQUARE;
        }
    }

    /**
     * Returns the type of the device, depending on its display size.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @return The type of the device as a value of the enum {@link DeviceType}. The type may either
     * be <code>PHONE</code>, <code>PHABLET</code> or <code>TABLET</code>
     */
    public static DeviceType getDeviceType(final Context context) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");

        return DeviceType.fromValue(context.getString(ResourceTable.String_device_type));
    }

    /**
     * Returns the width of the device's display.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @return The width of the device's display in pixels as an {@link Integer} value
     */
    public static int getDisplayWidth(final Context context) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return display.getAttributes().width;
    }

    /**
     * Returns the height of the device's display.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @return The height of the device's display in pixels as an {@link Integer} value
     */
    public static int getDisplayHeight(final Context context) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return display.getAttributes().height;
    }

    /**
     * Returns the logical density of the device's display.
     * <p>
     * This is a scaling factor for the density-independent pixel unit, where one DIP is one pixel
     * on an approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), providing the
     * baseline of the system's display. Thus on a 160dpi screen this density value will be 1; on a
     * 120 dpi screen it would be .75; etc.
     *
     * @param context The context, which should be used, as an instance of the class {@link Context}. The
     *                context may not be null
     * @return The logical density of the device's display as a {@link Float} value
     */
    public static float getDensity(final Context context) {
        Condition.INSTANCE.ensureNotNull(context, "The context may not be null");
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        return display.getAttributes().scalDensity;
    }
}
